﻿namespace MicroCloud.Log4Net
{
    /// <summary>
    /// log4net日志对象提供者
    /// </summary>
    public class Log4NetLoggerProvider : Disposable, ILoggerProvider
    {
        //字段
        private readonly ConcurrentDictionary<string, Log4NetLogger> _loggers = new();
        private readonly ILoggerRepository _loggerRepository;

        /// <summary>
        /// 初始化一个log4net日志对象提供者 <see cref="Log4NetLoggerProvider"/> 的新实例
        /// </summary>
        public Log4NetLoggerProvider()
            : this(Log4NetConfig.ConfigPath, Log4NetConfig.ConfigName)
        { }

        /// <summary>
        /// 初始化一个log4net日志对象提供者 <see cref="Log4NetLoggerProvider"/> 的新实例
        /// </summary>
        /// <param name="configPath">Log4Net日志config配置文件路径</param>
        /// <param name="configName">Log4Net日志config配置文件名称</param>
        public Log4NetLoggerProvider(string configPath, string configName)
        {
            Assembly assembly = Assembly.GetEntryAssembly() ?? GetCallingAssemblyFromStartup();
            _loggerRepository = LogManager.CreateRepository(assembly, typeof(Hierarchy));

            Log4NetConfig.ConfigPath = configPath ?? Log4NetConfig.ConfigPath;
            Log4NetConfig.ConfigName = configName ?? Log4NetConfig.ConfigName;

            if (!Log4NetConfig.InitLog4NetConfig(_loggerRepository))
            {
                //如果没有找到配置文件，则默认写一个保存目标，保存为文件。
                RollingFileAppender appender = new()
                {
                    Name = "root",
                    File = "Logs\\log_",
                    AppendToFile = true,
                    LockingModel = new FileAppender.MinimalLock(),
                    RollingStyle = RollingFileAppender.RollingMode.Date,
                    DatePattern = "yyyyMMdd-HH\".log\"",
                    StaticLogFileName = false,
                    MaxSizeRollBackups = 10,
                    Layout = new PatternLayout("[%d{HH:mm:ss.fff}] %-5p %c T%t:%n%m%n")
                };
                //其中layout节点的配置说明：
                //% m(message):输出的日志消息；
                //% n(newline):换行；
                //% d(datetime):输出当前语句运行的时刻；
                //% r(runtime):输出程序从运行到执行到当前语句时消耗的毫秒数；
                //% t(threadid):当前语句所在的线程ID ；
                //% p(priority): 日志的当前日志级别；
                //% c(class):当前日志对象的名称；
                //%L：输出语句所在的行号；
                //%F：输出语句所在的文件名； 
                //%-10：表示最小长度为10，如果不够，则用空格填充；
                //更多Layout参数请参考网址 http://logging.apache.org/log4net/index.html

                appender.ClearFilters();
                BasicConfigurator.Configure(_loggerRepository, appender);
                appender.ActivateOptions();
            }
        }

        /// <summary>
        /// 创建一个 <see cref="T:Microsoft.Extensions.Logging.ILogger" /> 的新实例
        /// </summary>
        /// <param name="categoryName">记录器生成的消息的类别名称。</param>
        /// <returns>日志实例</returns>
        public ILogger CreateLogger(string categoryName)
        {
            return _loggers.GetOrAdd(categoryName, key => new Log4NetLogger(_loggerRepository.Name, key));
        }

        /// <summary>
        /// 从启动时获取调用程序集
        /// </summary>
        /// <returns></returns>
        internal static Assembly GetCallingAssemblyFromStartup()
        {
            var stackTrace = new System.Diagnostics.StackTrace(2);
            for (int i = 0; i < stackTrace.FrameCount; i++)
            {
                var frame = stackTrace.GetFrame(i);
                var type = frame.GetMethod()?.DeclaringType;

                if (string.Equals(type?.Name, "Startup", StringComparison.OrdinalIgnoreCase))
                {
                    return type?.Assembly;
                }
            }
            return null;
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            if (!Disposed)
            {
                _loggers.Clear();
            }

            base.Dispose(disposing);
        }

    }
}